<?php

namespace app\index\controller;

use think\Controller;
use think\Db;
use think\db\Where;

class File extends Controller
{
    public function getFileList($filepath = null, $disktype = null)
    {
        if (!$filepath || !$disktype) {
            echo json([
                'errorMessage' => '字段信息不全',
                'success' => false
            ]);
        }
        $where = [];
        $where['filepath'] = $filepath;
        if ($disktype == 1) {
            $where['userid'] = session('userinfo.userId');
        }
        if ($disktype == 2) {
            $where['department'] = session('userinfo.department');
        }
        $where['disktype'] = $disktype;
        $list = Db::table('file')->where($where)->select();
        return json([
            'data' => $list,
            'success' => true
        ]);
    }

    public function selectFileByFileType($filetype = null, $disktype = null)
    {
        if (!$filetype || !$disktype) {
            return json([
                'errorMessage' => '字段信息不全',
                'success' => false
            ]);
        }
        $fileTypeArray = [
            1 => ['png', 'jpg', 'jpeg', 'img', 'gif'],
            2 => ['html', 'txt', 'pdf', 'rtf'],
            3 => ['mp4', 'avi'],
            4 => ['mp3']
        ];
        $where['extendname'] = ['in', $fileTypeArray[$filetype]];
        if ($disktype == 1) {
            $where['userid'] = session('userinfo.userId');
        }
        if ($disktype == 2) {
            $where['department'] = session('userinfo.department');
        }
        $where['disktype'] = $disktype;
        $list = Db::table('file')->where(new Where($where))->select();
        return json([
            'data' => $list,
            'success' => true
        ]);
    }

    public function getStorage($disktype = null)
    {
        if (!$disktype) {
            return json([
                'errorMessage' => '字段信息不全',
                'success' => false
            ]);
        }
        $where = [];
        if ($disktype == 1) {
            $where['userid'] = session('userinfo.userId');
        }
        if ($disktype == 2) {
            $where['department'] = session('userinfo.department');
        }
        $where['disktype'] = $disktype;
        $storage = Db::table('storage')->where($where)->find();
        return json([
            'data' => $storage,
            'success' => true
        ]);
    }

    //上传文件
    public function uploadFile()
    {
        $postData = input('post.');
        if (!isset($postData['filepath']) || !isset($postData['isdir']) || !isset($postData['disktype'])) {
            return json([
                'errorMessage' => '字段信息不全',
                'success' => false
            ]);
        }
        $data['filepath'] = $postData['filepath'];
        $data['isdir'] = $postData['isdir'];
        $data['userid'] = session('userinfo.userId');
        $data['department'] = session('userinfo.department');
        $data['disktype'] = $postData['disktype'];
        $name = $_FILES['file']['name'];
        $name = mb_substr($name, 0, mb_strrpos($name, '.'));
        $data['filename'] = $name;
        //检测同一目录下有无同名文件
        $other = Db::table('file')->where($data)->find();
        if ($other) {
            return json(['errorMessage' => "该目录下已存在同名文件！", 'success' => false]);
        }
        $data['filesize'] = $_FILES['file']['size'];
        $file = request()->file('file');
        $info = $file->move('../upload');
        if ($info) {
            $data['extendname'] = $info->getExtension();
            $data['fileurl'] = MY_UPLOAD_PATH . $info->getSaveName();
            $data['uploadtime'] = date('Y-m-d H:i:s');
            $re = Db::table('file')->insert($data);
            if ($re) {
                //在storage表里更新数据
                if ($data['disktype'] == 1) {
                    $where['userid'] = session('userinfo.userId');
                }
                if ($data['disktype'] == 2) {
                    $where['department'] = session('userinfo.department');
                }
                $where['disktype'] = $data['disktype'];
                $storage = Db::table('storage')->where($where)->find();
                if (!$storage) {
                    $where['storagesize'] = $data['filesize'];
                    Db::table('storage')->insert($where);
                } else {
                    $storage['storagesize'] += $data['filesize'];
                    Db::table('storage')->update($storage);
                }

                return json(['success' => true]);
            } else {
                return json(['errorMessage' => "数据库插入数据失败，请刷新重试", 'success' => false]);
            }
        } else {
            return json([
                'errorMessage' => $file->getError(),
                'success' => false
            ]);
        }
    }

    //新建文件夹
    public function createFile()
    {
        $postData = input('post.');
        if (!isset($postData['filepath']) || !isset($postData['filename']) || !isset($postData['isdir']) || !isset($postData['disktype'])) {
            return json([
                'errorMessage' => '字段信息不全',
                'success' => false
            ]);
        }
        $data['disktype'] = $postData['disktype'];
        $data['filepath'] = $postData['filepath'];
        $data['filename'] = $postData['filename'];
        $data['userid'] = session('userinfo.userId');
        $data['department'] = session('userinfo.department');
        $data['isdir'] = $postData['isdir'];
        //检测同一目录下有无同名文件夹
        $other = Db::table('file')->where(new Where($data))->find();
        if ($other) {
            return json(['errorMessage' => "该目录下已存在同名文件夹！", 'success' => false]);
        }
        $data['uploadtime'] = date('Y-m-d H:i:s');
        $re = Db::table('file')->insert($data);
        if ($re) {
            return json(['success' => true]);
        } else {
            return json(['errorMessage' => "数据库插入数据失败，请刷新重试", 'success' => false]);
        }
    }

    //获取文件夹树(移动文件时用)
    public function getFileTree($disktype = null, $file = null, $files = null)
    {
        if (!$disktype) {
            return json([
                'errorMessage' => '字段信息不全',
                'success' => false
            ]);
        }
        $where = [];
        $where['isdir'] = 1;
        if ($disktype == 1) {
            $where['userid'] = session('userinfo.userId');
        }
        if ($disktype == 2) {
            $where['department'] = session('userinfo.department');
        }
        $where['disktype'] = $disktype;
        $list = Db::table('file')->where($where)->select();
        foreach ($list as &$v) {
            if ($v['filepath'] === '/') {
                $v['pName'] = '/';
                $v['name'] = '/' . $v['filename'] . '/';
            } else {
                $v['pName'] = $v['filepath'];
                $v['name'] = $v['filepath'] . $v['filename'] . '/';
            }
            $v['attributes'] = ['filepath' => $v['name']];
            $v['label'] = $v['filename'];
        }
        $tree = $this->tree($list);
        if ($file) {
            $file = json_decode($file, true);
            $tree = $this->ridTree($tree, $file);
        } else {
            foreach ($files as &$file) {
                $file = json_decode($file, true);
                $tree = $this->ridTree($tree, $file, true);
            }
        }

        return json([
            'data' => [
                'label' => '/',
                'attributes' => [],
                'children' => $tree
            ],
            'success' => true
        ]);
    }

    //根据条件，剔除tree里的一些数据(防止移动文件夹时自己移到自己本身)
    public function ridTree($tree, $fileinfo, $resetStop = false)
    {
        if ($fileinfo['isdir'] == 0) {
            return $tree;
        }
        static $stop = false;
        if ($resetStop) {//需要多次剔除时，每次（的首次调用）需要重置这个局部静态变量
            $stop = false;
        }
        foreach ($tree as $i => &$v) {
            if ($v['fileid'] === $fileinfo['fileid']) {
                unset($tree[$i]);
                $tree = array_column($tree, null);
                $stop = true;
                break;
            } else {
                $v['children'] = $this->ridTree($v['children'], $fileinfo);
                if ($stop) {
                    break;
                }
            }
        }
        return $tree;
    }

    //将数组加功成tree的方法
    public function tree($list, $pName = '/', $name = 'children')
    {
        $arr = [];
        foreach ($list as $v) {
            if ($v['pName'] == $pName) {
                $v[$name] = $this->tree($list, $v['name']);
                $arr[] = $v;
            }
        }
        return $arr;
    }

    //单文件移动
    public function movefile($disktype, $extendname = null, $filename, $newfilepath, $oldfilepath)
    {
        if ($disktype > 1) {
            return json([
                'errorMessage' => '公共空间里的文件不允许移动！（允许上传，下载，删除空文件夹，以及删除本人传的文件）',
                'success' => false
            ]);
        }
        //检查，如果newfilepath与旧的filepath一样，则本质为原地移动，则不处理
        if ($oldfilepath == $newfilepath) {
            return json(['success' => true]);
        }

        $where = [];
        if ($disktype == 1) {
            $where['userid'] = session('userinfo.userId');
        }
        if ($disktype == 2) {
            $where['department'] = session('userinfo.department');
        }
        $where['disktype'] = $disktype;
        if ($extendname) {//移动文件
            $where['isdir'] = 0;
            $where['filename'] = $filename;
            $where['extendname'] = $extendname;
            $where['filepath'] = $oldfilepath;
            $re = Db::table('file')->where($where)->update(['filepath' => $newfilepath]);
            if ($re) {
                return json(['success' => true]);
            } else {
                return json([
                    'errorMessage' => '数据库修改失败，请刷新重试',
                    'success' => false
                ]);
            }
        } else {//移动文件夹
            $list = Db::table('file')->where($where)->select();
            $dir = $oldfilepath . $filename . '/';
            $this->updateMoveDir($list, $dir, $newfilepath, $where);
            return json(['success' => true]);
        }
    }

    //多文件移动
    public function batchMoveFile($disktype, $files, $newfilepath)
    {
        if ($disktype > 1) {
            return json([
                'errorMessage' => '公共空间里的文件不允许移动！（允许上传，下载，删除空文件夹，以及删除本人传的文件）',
                'success' => false
            ]);
        }
        $files = json_decode($files, true);
        //检查，如果newfilepath与旧的filepath一样，则本质为原地移动，则不处理
        if ($files[0]['filepath'] == $newfilepath) {
            return json(['success' => true]);
        }
        foreach ($files as $file) {
            $this->movefile($disktype, $file['extendname'], $file['filename'], $newfilepath, $file['filepath']);
        }
        return json(['success' => true]);
    }

    //根据传入的所有文件数组和目标文件夹路径，更新目标文件夹及其子文件的信息
    public function updateMoveDir($list, $dir, $newfilepath, $where)
    {
        foreach ($list as &$v) {
            if ($v['filepath'] === '/') {
                $v['pName'] = '/';
                $v['name'] = '/' . $v['filename'] . '/';
            } else {
                $v['pName'] = $v['filepath'];
                $v['name'] = $v['filepath'] . $v['filename'] . '/';
            }
            if ($v['name'] === $dir) {
                $mother = $v;
            }
        }
        $mother['children'] = $this->tree($list, $dir);
        $this->doUpdateTree($mother, $newfilepath, $where);
    }

    //更新文件树
    public function doUpdateTree($mother, $newfilepath, $where)
    {
        //移到之前，为实现同名文件夹内容融合的效果，尝试删除可能存在的同名文件夹字段
        Db::table('file')->where($where)->where('filename', $mother['filename'])->where('filepath', $newfilepath)
            ->where('isdir', $mother['isdir'])->where('filesize', 0)->delete();
        Db::table('file')->where('fileid', $mother['fileid'])->update(['filepath' => $newfilepath]);
        foreach ($mother['children'] as $v) {
            $this->doUpdateTree($v, $newfilepath . $mother['filename'] . '/', $where);
        }
    }

    //单文件删除
    public function deleteFile($fileinfo = null)
    {
        $fileinfo = $fileinfo ? $fileinfo : input('post.');
        //如果是公共空间的文件，则必须是自己的文件才能删除，，如果是公共空间的文件夹，必须是空文件夹才能删除
        if ($fileinfo['disktype'] > 1) {
            if ($fileinfo['isdir'] == 0) {
                if ($fileinfo['userid'] != session('userinfo.userId')) {
                    return json([
                        'errorMessage' => '公共空间里只能删除本人上传的文件',
                        'success' => false
                    ]);
                }
            } else {
                //如果是文件夹，判断它的下一层有无文件(夹)，没有才视为空
                if ($fileinfo['disktype'] == 2) {
                    $where1['department'] = session('userinfo.department');
                }
                $where1['disktype'] = $fileinfo['disktype'];
                $where1['filepath'] = $fileinfo['filepath'] . $fileinfo['filename'] . '/';
                $child = Db::table('file')->where($where1)->find();
                if ($child) {
                    return json([
                        'errorMessage' => '公共空间里只允许删除空文件夹',
                        'success' => false
                    ]);
                }
            }
        }
        if ($fileinfo['disktype'] == 1) {
            $where['userid'] = session('userinfo.userId');
        }
        if ($fileinfo['disktype'] == 2) {
            $where['department'] = session('userinfo.department');
        }
        $where['disktype'] = $fileinfo['disktype'];
        if ($fileinfo['isdir'] == 0) {//如果删除一个文件
            $this->deleteSingleFile($fileinfo, $where);
            return json(['success' => true]);
        } else {//如果删除一个文件夹
            $list = Db::table('file')->where(new Where($where))->select();
            $dir = $fileinfo['filepath'] . $fileinfo['filename'] . '/';
            foreach ($list as &$v) {
                if ($v['filepath'] === '/') {
                    $v['pName'] = '/';
                    $v['name'] = '/' . $v['filename'] . '/';
                } else {
                    $v['pName'] = $v['filepath'];
                    $v['name'] = $v['filepath'] . $v['filename'] . '/';
                }
                if ($v['name'] === $dir) {
                    $mother = $v;
                }
            }
            $mother['children'] = $this->tree($list, $dir);
            $this->deleteTree($mother, $where);
            return json(['success' => true]);
        }
    }

    //多文件删除
    public function batchDeleteFile($disktype, $files)
    {
        $msg = "";
        $hasError = false;
        $files = json_decode($files, true);
        foreach ($files as $file) {
            $file['disktype'] = $disktype;
            $re = $this->deleteFile($file);
            if ($re->data['success'] == false) {
                $msg .= ($re->data['errorMessage'] . '“' . $file['filename'] . '“删除失败；');
                $hasError = true;
            }
        }
        return $hasError ? json(['success' => false, 'errorMessage' => $msg]) : json(['success' => true]);
    }

    public function deleteTree($mother, $where)
    {
        $this->deleteSingleFile($mother, $where);
        foreach ($mother['children'] as $v) {
            $this->deleteTree($v, $where);
        }
    }

    //删除单一文件(夹)
    public function deleteSingleFile($fileinfo, $where)
    {
        $path = str_replace(MY_SERVER_NAME, '..', $fileinfo['fileurl']);
        if (is_file($path)) {
            unlink($path);
        } else {
            $this->delDir($path);
        }
        Db::table('storage')->where(new Where($where))->dec('storagesize', $fileinfo['filesize'])->update();
        Db::table('file')->where('fileid', $fileinfo['fileid'])->delete();
    }

    //删除一个目录及其所包含所有文件
    protected function delDir($path)
    {
        if (is_dir($path)) {
            $p = scandir($path);
            foreach ($p as $val) {
                if ($val != "." && $val != "..") {
                    if (is_dir($path . '/' . $val)) {
                        $this->delDir($path . '/' . $val);
                    } else {
                        unlink($path . '/' . $val);
                    }
                }
            }
            @rmdir($path);
        }
    }

    //解压文件
    public function unzipFile()
    {
        $fileinfo = input('post.');
        if (session('userinfo.userId') != $fileinfo['userid']) {
            return json([
                'errorMessage' => '公共空间只允许解压自己上传的文件',
                'success' => false
            ]);
        }
        $filepath = $fileinfo['filepath'];
        $where['userid'] = session('userinfo.userId');
        $where['department'] = session('userinfo.department');
        $where['disktype'] = $fileinfo['disktype'];
        $filePath = str_replace(MY_SERVER_NAME, '..', $fileinfo['fileurl']);
        $targetPath = mb_substr($filePath, 0, mb_strrpos($filePath, DIRECTORY_SEPARATOR)) . '/' . $fileinfo['filename'];
        if (is_dir($targetPath)) {
            if (array_diff(scandir($targetPath), array('..', '.')) == []) {
                rmdir($targetPath);
            } else {
                return json(['errorMessage' => "该空间里尚存在被解压出的文件夹，如想再次解压，请删除所有旧文件夹！", 'success' => false]);
            }
        }
        $re = $this->unzip($filePath, $targetPath);
        if (!$re) {
            return json(['errorMessage' => "解压失败，请重试！", 'success' => false]);
        }
        $data = $where;
        $data['uploadtime'] = date('Y-m-d H:i:s');
        $insertData = $this->makeScanInsertData($targetPath, $data, $filepath);
        Db::table('file')->insertAll($insertData);
        $totalFileSize = 0;
        foreach ($insertData as $v) {
            $totalFileSize += $v['filesize'];
        }
        Db::table('storage')->where($where)->inc('storagesize', $totalFileSize)->update();
        return json(['success' => $re]);
    }

    //处理解压扫描出的文件数组，形成树形数据
    public function makeScanInsertData($targetPath, $data, $filepath, $result = [])
    {
        $dirArr = scandir($targetPath);
        foreach ($dirArr as $v) {
            if ($v != '.' && $v != '..') {
                $realFilePath = $targetPath . DIRECTORY_SEPARATOR . $v;
                $data['filepath'] = $filepath;
                $data['isdir'] = is_dir($realFilePath) ? 1 : 0;
                $data['filename'] = $data['isdir'] == 0 ? mb_substr($v, 0, mb_strrpos($v, '.')) : $v;
                $data['extendname'] = $data['isdir'] == 1 ? null : pathinfo($v, PATHINFO_EXTENSION);
                $data['filesize'] = filesize($realFilePath);
                $data['fileurl'] = str_replace('..', MY_SERVER_NAME, $realFilePath);
                $result[] = $data;
                if ($data['isdir'] == 1) {
                    $result = $this->makeScanInsertData($realFilePath, $data, $filepath . $v . '/', $result);
                }
            }
        }
        return $result;
    }

    /**
     * 解压方法
     * @param $filePath 压缩包所在地址
     * @param $path 解压路径
     * @return bool
     */
    public function unzip($filePath, $path)
    {
        if (empty($path) || empty($filePath)) {
            return false;
        }

        $zip = new \ZipArchive();

        if ($zip->open($filePath) === true) {
            $zip->extractTo($path);
            $zip->close();
            return true;
        } else {
            return false;
        }
    }
}
